#include "pch.h"
#include "Geometries.h"
#include "Factories.h"
#include "ErrorCodes.h"
#include "StrokeStyle.h"

using namespace Platform;
using namespace Windows::Foundation;
using namespace concurrency;
using namespace Microsoft::WRL;

using namespace DXBase;
using namespace DXBase::D2D;

GeometrySink^ PathGeometry::Open(FILL_MODE fill)
{
	pgeom = nullptr;
	ngeom = nullptr;

	HR2Ex(Factories::GetD2DFactory()->CreatePathGeometry(&pgeom));
	ngeom = ref new NativeGeometry(pgeom);

	ComPtr<ID2D1GeometrySink> sink;
	HR2Ex(pgeom->Open(&sink));
	sink->SetFillMode(static_cast<D2D1_FILL_MODE>(fill));
	return ref new GeometrySink(sink);
}
NativeGeometry^ PathGeometry::GetNativeGeometry()
{
	// create an empty path geometry if needed (will work with Combine and such)
	if (!ngeom)
	{
		HR2Ex(Factories::GetD2DFactory()->CreatePathGeometry(&pgeom));
		ngeom = ref new NativeGeometry(pgeom);
	}
	return ngeom; 
}
UINT32 PathGeometry::FigureCount::get()
{
	if (!pgeom)
		return 0;
	UINT32 result;
	HR2Ex(pgeom->GetFigureCount(&result));
	return result;
}
UINT32 PathGeometry::SegmentCount::get()
{
	if (!pgeom)
		return 0;
	UINT32 result;
	HR2Ex(pgeom->GetSegmentCount(&result));
	return result;
}

static inline D2D1_BEZIER_SEGMENT makeD2D(const BEZIER_SEGMENT& seg)
{
	D2D1_BEZIER_SEGMENT result = { makeD2D(seg.p1), makeD2D(seg.p2), makeD2D(seg.p3) };
	return result;
}
static inline D2D1_QUADRATIC_BEZIER_SEGMENT makeD2D(const QUADRATIC_BEZIER_SEGMENT& qbz)
{
	D2D1_QUADRATIC_BEZIER_SEGMENT result = { makeD2D(qbz.p1), makeD2D(qbz.p2) };
	return result;
}
static inline D2D1_ARC_SEGMENT makeD2D(const ARC_SEGMENT& arc)
{
	D2D1_ARC_SEGMENT result = { 
		makeD2D(arc.point), makeD2D(arc.size), arc.rotationAngle, 
		static_cast<D2D1_SWEEP_DIRECTION>(arc.sweepDirection), static_cast<D2D1_ARC_SIZE>(arc.arcSize) 
	};
	return result;
}

ComPtr<ID2D1GeometrySink> GeometrySink::GetNative()
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	return sink;
}
void GeometrySink::Close()
{
	if (sink)
		HR2Ex(sink->Close());
	sink = nullptr;
}
void GeometrySink::SetSegmentFlags(PATH_SEGMENT flags)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->SetSegmentFlags(static_cast<D2D1_PATH_SEGMENT>(flags));
}
void GeometrySink::BeginFigure(float2 startPoint, FIGURE_BEGIN begin)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->BeginFigure(makeD2D(startPoint), static_cast<D2D1_FIGURE_BEGIN>(begin));
}
void GeometrySink::EndFigure(FIGURE_END figureEnd)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->EndFigure(static_cast<D2D1_FIGURE_END>(figureEnd));
}
void GeometrySink::AddBeziers(Windows::Foundation::Collections::IIterable<BEZIER_SEGMENT>^ beziers)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	std::vector<D2D1_BEZIER_SEGMENT> data;
	for each (auto b in beziers)
		data.push_back(::makeD2D(b));
	sink->AddBeziers(&data[0], (UINT32)data.size());
}
void GeometrySink::AddLines(Windows::Foundation::Collections::IIterable<float2>^ points)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	std::vector<D2D1_POINT_2F> data;
	for each (auto b in points)
		data.push_back(makeD2D(b));
	sink->AddLines(&data[0], (UINT32)data.size());
}
void GeometrySink::AddArc(ARC_SEGMENT arc)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->AddArc(::makeD2D(arc));
}
void GeometrySink::AddBezier(BEZIER_SEGMENT bezier)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->AddBezier(::makeD2D(bezier));
}
void GeometrySink::AddLine(float2 p)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->AddLine(makeD2D(p));
}
void GeometrySink::AddQuadraticBezier(QUADRATIC_BEZIER_SEGMENT qbs)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	sink->AddQuadraticBezier(::makeD2D(qbs));
}
void GeometrySink::AddQuadraticBeziers(Windows::Foundation::Collections::IIterable<QUADRATIC_BEZIER_SEGMENT>^ qbs)
{
	if (!sink)
		throw ExHelper::CreateException(ErrorCodes::Closed);
	std::vector<D2D1_QUADRATIC_BEZIER_SEGMENT> data;
	for each (auto b in qbs)
		data.push_back(::makeD2D(b));
	sink->AddQuadraticBeziers(&data[0], (UINT32)data.size());
}
void GeometrySink::CombineGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry, COMBINE_MODE combineMode)
{
	if (!srcGeometry || !inputGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->CombineWithGeometry(
		inputGeometry->GetNativeGeometry()->mGeom.Get(), 
		static_cast<D2D1_COMBINE_MODE>(combineMode),
		nullptr,
		GetNative().Get()));
}
void GeometrySink::CombineGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry, COMBINE_MODE combineMode, float3x2 inputGeometryTransform)
{
	if (!srcGeometry || !inputGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->CombineWithGeometry(
		inputGeometry->GetNativeGeometry()->mGeom.Get(), 
		static_cast<D2D1_COMBINE_MODE>(combineMode),
		makeD2D(inputGeometryTransform),
		GetNative().Get()));
}


GEOMETRY_RELATION Geometry::CompareGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry)
{
	if (!srcGeometry || !inputGeometry)
		throw ref new NullReferenceException();
	D2D1_GEOMETRY_RELATION result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->CompareWithGeometry(
		inputGeometry->GetNativeGeometry()->mGeom.Get(),
		nullptr,
		&result));
	return static_cast<GEOMETRY_RELATION>(result);
}
GEOMETRY_RELATION Geometry::CompareGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry, float3x2 inputGeometryTransform)
{
	if (!srcGeometry || !inputGeometry)
		throw ref new NullReferenceException();
	D2D1_GEOMETRY_RELATION result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->CompareWithGeometry(
		inputGeometry->GetNativeGeometry()->mGeom.Get(),
		makeD2D(inputGeometryTransform),
		&result));
	return static_cast<GEOMETRY_RELATION>(result);
}
float Geometry::Length(IGeometry^ srcGeometry)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeLength(nullptr, &result));
	return result;
}
float Geometry::Length(IGeometry^ srcGeometry, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeLength(nullptr, flatteningTolerance, &result));
	return result;
}
float Geometry::Length(IGeometry^ srcGeometry, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeLength(makeD2D(worldTransform), flatteningTolerance, &result));
	return result;
}
float Geometry::Length(IGeometry^ srcGeometry, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeLength(makeD2D(worldTransform), &result));
	return result;
}
float Geometry::Area(IGeometry^ srcGeometry)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeArea(nullptr, &result));
	return result;
}
float Geometry::Area(IGeometry^ srcGeometry, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeArea(nullptr, flatteningTolerance, &result));
	return result;
}
float Geometry::Area(IGeometry^ srcGeometry, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeArea(makeD2D(worldTransform), flatteningTolerance, &result));
	return result;
}
float Geometry::Area(IGeometry^ srcGeometry, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	float result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->ComputeArea(makeD2D(worldTransform), &result));
	return result;
}
bool Geometry::TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_POINT_2F dpoint, dtangent;
	HRESULT hr = srcGeometry->GetNativeGeometry()->mGeom->ComputePointAtLength(length, nullptr, &dpoint, &dtangent);
	if (FAILED(hr))
		return false;
	if (point)
	{
		point->x = dpoint.x;
		point->y = dpoint.y;
	}
	if (tangent)
	{
		tangent->x = dtangent.x;
		tangent->y = dtangent.y;
	}
	return true;
}
bool Geometry::TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_POINT_2F dpoint, dtangent;
	HRESULT hr = srcGeometry->GetNativeGeometry()->mGeom->ComputePointAtLength(length, nullptr, flatteningTolerance, &dpoint, &dtangent);
	if (FAILED(hr))
		return false;
	if (point)
	{
		point->x = dpoint.x;
		point->y = dpoint.y;
	}
	if (tangent)
	{
		tangent->x = dtangent.x;
		tangent->y = dtangent.y;
	}
	return true;
}
bool Geometry::TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_POINT_2F dpoint, dtangent;
	HRESULT hr = srcGeometry->GetNativeGeometry()->mGeom->ComputePointAtLength(length, makeD2D(worldTransform), flatteningTolerance, &dpoint, &dtangent);
	if (FAILED(hr))
		return false;
	if (point)
	{
		point->x = dpoint.x;
		point->y = dpoint.y;
	}
	if (tangent)
	{
		tangent->x = dtangent.x;
		tangent->y = dtangent.y;
	}
	return true;
}
bool Geometry::TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_POINT_2F dpoint, dtangent;
	HRESULT hr = srcGeometry->GetNativeGeometry()->mGeom->ComputePointAtLength(length, makeD2D(worldTransform), &dpoint, &dtangent);
	if (FAILED(hr))
		return false;
	if (point)
	{
		point->x = dpoint.x;
		point->y = dpoint.y;
	}
	if (tangent)
	{
		tangent->x = dtangent.x;
		tangent->y = dtangent.y;
	}
	return true;
}
bool Geometry::FillContains(IGeometry^ srcGeometry, float2 p)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->FillContainsPoint(makeD2D(p), nullptr, &result));
	return result != FALSE;
}
bool Geometry::FillContains(IGeometry^ srcGeometry, float2 p, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->FillContainsPoint(makeD2D(p), nullptr, flatteningTolerance, &result));
	return result != FALSE;
}
bool Geometry::FillContains(IGeometry^ srcGeometry, float2 p, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->FillContainsPoint(makeD2D(p), makeD2D(worldTransform), flatteningTolerance, &result));
	return result != FALSE;
}
bool Geometry::FillContains(IGeometry^ srcGeometry, float2 p, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->FillContainsPoint(makeD2D(p), makeD2D(worldTransform), &result));
	return result != FALSE;
}
bool Geometry::StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->StrokeContainsPoint(makeD2D(p), width, pStyle, nullptr, &result));
	return result != FALSE;
}
bool Geometry::StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->StrokeContainsPoint(makeD2D(p), width, pStyle, nullptr, flatteningTolerance, &result));
	return result != FALSE;
}
bool Geometry::StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->StrokeContainsPoint(makeD2D(p), width, pStyle, makeD2D(worldTransform), flatteningTolerance, &result));
	return result != FALSE;
}
bool Geometry::StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	BOOL result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->StrokeContainsPoint(makeD2D(p), width, pStyle, makeD2D(worldTransform), &result));
	return result != FALSE;
}
Windows::Foundation::Rect Geometry::Bounds(IGeometry^ srcGeometry)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_RECT_F result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->GetBounds(nullptr, &result));
	return Windows::Foundation::Rect(result.left, result.top, result.right - result.left, result.bottom - result.top);
}
Windows::Foundation::Rect Geometry::Bounds(IGeometry^ srcGeometry, float3x2 inputGeometryTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_RECT_F result;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->GetBounds(makeD2D(inputGeometryTransform), &result));
	return Windows::Foundation::Rect(result.left, result.top, result.right - result.left, result.bottom - result.top);
}
Windows::Foundation::Rect Geometry::WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_RECT_F result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->GetWidenedBounds(width, pStyle, nullptr, &result));
	return Windows::Foundation::Rect(result.left, result.top, result.right - result.left, result.bottom - result.top);
}
Windows::Foundation::Rect Geometry::WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_RECT_F result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->GetWidenedBounds(width, pStyle, nullptr, flatteningTolerance, &result));
	return Windows::Foundation::Rect(result.left, result.top, result.right - result.left, result.bottom - result.top);
}
Windows::Foundation::Rect Geometry::WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_RECT_F result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->GetWidenedBounds(width, pStyle, makeD2D(worldTransform), flatteningTolerance, &result));
	return Windows::Foundation::Rect(result.left, result.top, result.right - result.left, result.bottom - result.top);
}
Windows::Foundation::Rect Geometry::WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	D2D1_RECT_F result;
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->GetWidenedBounds(width, pStyle, makeD2D(worldTransform), &result));
	return Windows::Foundation::Rect(result.left, result.top, result.right - result.left, result.bottom - result.top);
}
void GeometrySink::Outline(IGeometry^ srcGeometry)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Outline(nullptr, GetNative().Get()));
}
void GeometrySink::Outline(IGeometry^ srcGeometry, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Outline(nullptr, flatteningTolerance, GetNative().Get()));
}
void GeometrySink::Outline(IGeometry^ srcGeometry, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Outline(makeD2D(worldTransform), flatteningTolerance, GetNative().Get()));
}
void GeometrySink::Outline(IGeometry^ srcGeometry, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Outline(makeD2D(worldTransform), GetNative().Get()));
}
void GeometrySink::Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Simplify(static_cast<D2D1_GEOMETRY_SIMPLIFICATION_OPTION>(option), nullptr, GetNative().Get()));
}
void GeometrySink::Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Simplify(static_cast<D2D1_GEOMETRY_SIMPLIFICATION_OPTION>(option), nullptr, flatteningTolerance, GetNative().Get()));
}
void GeometrySink::Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Simplify(static_cast<D2D1_GEOMETRY_SIMPLIFICATION_OPTION>(option), makeD2D(worldTransform), flatteningTolerance, GetNative().Get()));
}
void GeometrySink::Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Simplify(static_cast<D2D1_GEOMETRY_SIMPLIFICATION_OPTION>(option), makeD2D(worldTransform), GetNative().Get()));
}
void GeometrySink::Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Widen(strokeWidth, pStyle, nullptr, GetNative().Get()));
}
void GeometrySink::Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Widen(strokeWidth, pStyle, nullptr, flatteningTolerance, GetNative().Get()));
}
void GeometrySink::Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style, float3x2 worldTransform, float flatteningTolerance)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Widen(strokeWidth, pStyle, makeD2D(worldTransform), flatteningTolerance, GetNative().Get()));
}
void GeometrySink::Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style, float3x2 worldTransform)
{
	if (!srcGeometry)
		throw ref new NullReferenceException();
	ID2D1StrokeStyle* pStyle = style ? style->GetNativeStrokeStyle().Get() : nullptr;
	HR2Ex(srcGeometry->GetNativeGeometry()->mGeom->Widen(strokeWidth, pStyle, makeD2D(worldTransform), GetNative().Get()));
}
void GeometrySink::AddPathGeometry(PathGeometry^ g)
{
	if (!g)
		throw ref new NullReferenceException();
	g->GetNativeGeometry();
	g->pgeom->Stream(GetNative().Get());
}

RectangleGeometry::RectangleGeometry(Windows::Foundation::Rect rect) : rect(rect)
{
	HR2Ex(Factories::GetD2DFactory()->CreateRectangleGeometry(DXBase::makeD2D(rect), pgeom.GetAddressOf()));
	ngeom = ref new NativeGeometry(pgeom);
}
RoundedRectangleGeometry::RoundedRectangleGeometry(Windows::Foundation::Rect rect, float radX, float radY) 
	: rect(rect), radX(radX), radY(radY)
{
	D2D1_ROUNDED_RECT rrr = { DXBase::makeD2D(rect), radX, radY };
	HR2Ex(Factories::GetD2DFactory()->CreateRoundedRectangleGeometry(rrr, pgeom.GetAddressOf()));
	ngeom = ref new NativeGeometry(pgeom);
}
EllipseGeometry::EllipseGeometry(Windows::Foundation::Rect rect) 
	: rect(rect)
{
	D2D1_ELLIPSE e = { { rect.X + rect.Width / 2, rect.Y + rect.Height / 2 }, rect.Width / 2, rect.Height / 2 };
	HR2Ex(Factories::GetD2DFactory()->CreateEllipseGeometry(e, pgeom.GetAddressOf()));
	ngeom = ref new NativeGeometry(pgeom);
}
GeometryGroup::GeometryGroup(Windows::Foundation::Collections::IIterable<IGeometry^>^ geoms) 
	: fill(FILL_MODE::Alternate), geoms(geoms)
{
	std::vector<ID2D1Geometry*> ngeoms;
	if (geoms)
		for each(auto g in geoms)
			ngeoms.push_back(g->GetNativeGeometry()->mGeom.Get());
	HR2Ex(Factories::GetD2DFactory()->CreateGeometryGroup(static_cast<D2D1_FILL_MODE>(fill), &ngeoms[0], (UINT32)ngeoms.size(), pgeom.GetAddressOf()));
	ngeom = ref new NativeGeometry(pgeom);
}
GeometryGroup::GeometryGroup(FILL_MODE fill, Windows::Foundation::Collections::IIterable<IGeometry^>^ geoms) 
	: fill(fill), geoms(geoms)
{
	std::vector<ID2D1Geometry*> ngeoms;
	if (geoms)
		for each(auto g in geoms)
			ngeoms.push_back(g->GetNativeGeometry()->mGeom.Get());
	HR2Ex(Factories::GetD2DFactory()->CreateGeometryGroup(static_cast<D2D1_FILL_MODE>(fill), &ngeoms[0], (UINT32)ngeoms.size(), pgeom.GetAddressOf()));
	ngeom = ref new NativeGeometry(pgeom);
}
TransformedGeometry::TransformedGeometry(float3x2 transform, IGeometry^ geom) 
	: transform(transform), source(geom)
{
	if (!geom)
		throw ref new NullReferenceException();
	HR2Ex(Factories::GetD2DFactory()->CreateTransformedGeometry(geom->GetNativeGeometry()->mGeom.Get(), makeD2D(transform), pgeom.GetAddressOf()));
	ngeom = ref new NativeGeometry(pgeom);
}
